package com.cgly.medical.security;

import com.cgly.medical.entity.AccountModel;
//import com.cgly.medical.filter.JwtFilter;
import com.cgly.medical.service.impl.UserDetailsServiceImpl;
import com.cgly.medical.util.MD5Utils;
import com.cgly.medical.util.Msg;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.config.annotation.ObjectPostProcessor;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.session.SessionRegistryImpl;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
import org.springframework.security.web.authentication.session.ConcurrentSessionControlAuthenticationStrategy;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * @program: medical
 * @description: spring security 配置类
 * @author: chen di
 * @create: 2022-08-15 16:40
 */

@Configuration
@EnableWebSecurity //Spring Security用于启用web安全的注解
//@EnableGlobalMethodSecurity(prePostEnabled = true)//开启授权
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

//    @Resource
//    JwtFilter jwtFilter;

    @Resource
    MyAuthenticationSuccessHandler authenticationSuccessHandler;

    @Resource
    MyAuthenticationFailHandler authenticationFailHandler;

    @Autowired
    CustomUrlDecisionManager customUrlDecisionManager;

    @Autowired
    CustomFilterInvocationSecurityMetadataSource customFilterInvocationSecurityMetadataSource;

    @Resource
    TokenVerifyFilter tokenVerifyFilter;

    @Bean
    SessionRegistryImpl sessionRegistry() {
        return new SessionRegistryImpl();
    }
//    @Resource
//    CustomizeAuthenticationEntryPoint authenticationEntryPoint;

//    @Resource
//    MyAccessDeniedHandler accessDeniedHandler;

    @Resource
    UserDetailsServiceImpl myUserService;

    @Resource
    MyAuthenticationProvider myAuthenticationProvider;

//
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/resources/**",
                "/v2/**",
                "/swagger-resources/**",
                "/swagger-resources",
                "/swagger-resources/configuration/security",
                "/swagger-ui.html",
                "/webjars/**",
                "/css/**",
                "/js/**",
                "/index.html",
                "/img/**",
                "/fonts/**",
                "/favicon.ico",
                "/verifyCode",
                "/patient/addORupdate"
                );
    }
    /**
     * 身份认证接口
     *
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {


        //自定义认证逻辑：可以传入Myservice
        auth.userDetailsService(myUserService);
        auth.authenticationProvider(myAuthenticationProvider);

    }


    /**
     * 重写configure(HtpSecurity htp)方法,用于配置拦截器，登录接口，登录成功处理器，登录失败处理器，登录异常处理器，
     * 登录失效或者未登录处理器，无权限处理器，配置开放接口，session设置以及跨域设置，
     * hasRole()：拥有指定角色才能够访问
     * hasAnyRole()：拥有任意一个角色就可以访问
     * @param http
     * @throws Exception
     */
    //授权规则配置
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.csrf().disable()
                .cors().configurationSource(configurationSource());//关闭csrf跨域检查


        //用重写的Filter替换掉原有的UsernamePasswordAuthenticationFilter
        http.addFilterAt(customAuthenticationFilter(),
                UsernamePasswordAuthenticationFilter.class);


        //token验证过滤器
        http.addFilterBefore(tokenVerifyFilter, UsernamePasswordAuthenticationFilter.class);


        http.authorizeRequests()
                .withObjectPostProcessor(new ObjectPostProcessor<FilterSecurityInterceptor>() {
                    @Override
                    public <O extends FilterSecurityInterceptor> O postProcess(O object) {
                        object.setAccessDecisionManager(customUrlDecisionManager);
                        object.setSecurityMetadataSource(customFilterInvocationSecurityMetadataSource);
                        return object;
                    }
                })
                .and()
                .logout()
                .logoutSuccessHandler((req, resp, authentication) -> {
                    resp.setContentType("application/json;charset=utf-8");
                    PrintWriter out = resp.getWriter();
                    out.write(new ObjectMapper().writeValueAsString(Msg.success().mess("注销成功!")));
                    out.flush();
                    out.close();
                }
        )
                .permitAll();



    }

    //获取跨域配置
    public UrlBasedCorsConfigurationSource configurationSource() {
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.addAllowedHeader("*");
        corsConfiguration.addAllowedMethod("*");
        corsConfiguration.addAllowedOrigin("*");
//        corsConfiguration.setAllowCredentials(true);
        //前后端分离项目需要携带cookie时，需要这句，但加上后origin里就不能为”*“

        corsConfiguration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**", corsConfiguration);
        return source;
    }



    //注册自定义的UsernamePasswordAuthenticationFilter
    @Bean
    CustomAuthenticationFilter customAuthenticationFilter() throws Exception {
        CustomAuthenticationFilter filter = new CustomAuthenticationFilter();
        filter.setAuthenticationSuccessHandler(authenticationSuccessHandler);
        filter.setAuthenticationFailureHandler(authenticationFailHandler);

        filter.setFilterProcessesUrl("/login");
        //这句很关键，重用WebSecurityConfigurerAdapter配置的AuthenticationManager，不然要自己组装AuthenticationManager
        filter.setAuthenticationManager(authenticationManagerBean());
        ConcurrentSessionControlAuthenticationStrategy sessionStrategy = new ConcurrentSessionControlAuthenticationStrategy(sessionRegistry());
        sessionStrategy.setMaximumSessions(1);
        filter.setSessionAuthenticationStrategy(sessionStrategy);
        return filter;
    }
}















